Load libraries

source("../General/parameters.R")
source("../General/rmb_functions.R")
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse 1.3.0 ──
✓ ggplot2 3.3.2.9000     ✓ purrr   0.3.4     
✓ tibble  3.0.1          ✓ dplyr   1.0.0     
✓ tidyr   1.1.0          ✓ stringr 1.4.0     
✓ readr   1.3.1          ✓ forcats 0.5.0     
── Conflicts ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
x dplyr::filter() masks stats::filter()
x dplyr::lag()    masks stats::lag()
library(cowplot)

********************************************************
Note: As of version 1.0.0, cowplot does not change the
  default ggplot2 theme anymore. To recover the previous
  behavior, execute:
  theme_set(theme_cowplot())
********************************************************
library(randomForest)
randomForest 4.6-14
Type rfNews() to see new features/changes/bug fixes.

Attaching package: ‘randomForest’

The following object is masked from ‘package:dplyr’:

    combine

The following object is masked from ‘package:ggplot2’:

    margin
library(tidyverse)

Load data

map <- readRDS("../Data/drought_map.RDS")
tax <- readRDS("../Data/tax.RDS")
otu <- readRDS("../Data/otu_pers.RDS") 
train.set <- readRDS("../Data/rf_results.RDS") %>% 
  mutate(Compartment = fct_recode(Compartment, 
                                  "Rhizosphere" = "RS",
                                  "Endosphere" = "ES")) %>% 
  mutate(Compartment = fct_relevel(Compartment, "Rhizosphere"))
  
all.clust.summary <- readRDS("../Data/drought_clusters.RDS")

Load data

## Get log-transformed relative abundances for the OTU table
otu <- rel_ab(otu)
otu <- log_norm(otu)

map <- mutate(map, Set = ifelse(Treatment2 == "WC_TRN", "Train", "Test"))

# The predictors need to be formatted as a data frame or matrix
get_predictors <- function(x){
  select(x, -(SampleID:Age))
} 

# The response variable needs to be formatted as a vector
get_response <- function(x) {
  select(x, Age) %>% 
  .$Age
}  

whole.set <- as.data.frame(t(otu)) %>%  
  mutate(SampleID = rownames(.)) %>% 
  inner_join(select(map, SampleID, Compartment, Set, Treatment, Age), by = "SampleID") %>% 
  group_by(Set, Compartment, Treatment) %>% 
  nest() %>% 
  mutate(otu = map(data, get_predictors),
         age = map(data, get_response)) %>% 
  mutate(Compartment = fct_recode(Compartment, 
                                  "Rhizosphere" = "RS",
                                  "Endosphere" = "ES")) %>% 
  mutate(Compartment = fct_relevel(Compartment, "Rhizosphere"))
trt.lines <- data.frame(Treatment = c("DS1", "DS2", "DS3"),
                        Treatment2 = c("DS1", "DS2", "DS3"),
                        Contrast = c("WC vs DS1", "WC vs DS2", "WC vs DS3"),
                        IniTreatment = c(4.5, 4.5, 4.5),
                        EndTreatment = c(5.5, 6.5, 7.5),
                        IniTreatment2 = c(41,41,41),
                        EndTreatment2 = c(52,62,74))
cv.plot <- train.set %>% 
  unnest(cv, nOTU) %>% 
  mutate(Compartment = fct_relevel(Compartment, "RS")) %>% 
  mutate(Compartment = fct_recode(Compartment, Rhizosphere = "RS", Endosphere = "ES")) %>% 
  ggplot(aes(nOTU, Error, color = Compartment)) +
  geom_line(size =1) +
  geom_point(aes(alpha = minError), size = 10) +
  geom_text(aes(label = nOTU, alpha = minError), size = 6, color = "black") +
  scale_color_manual(values = rev(cmp.pal)) +
  scale_alpha_manual(values = c(0,1)) +
  guides(alpha = F) +
  scale_x_log10() +
  theme_classic() +
  theme(text = element_text(size = 15),
        legend.position = c(0.5, 0.8),
        legend.title = element_blank()) 
unnest() has a new interface. See ?unnest for details.
Try `df %>% unnest(c(cv, nOTU))`, with `mutate()` if neededUnknown levels in `f`: RSUnknown levels in `f`: RS, ES
cv.plot

Check taxonomy of the OTUs

rf.ranks <- train.set %>% 
  unnest(imp.tax) %>% 
  mutate(cmpOTU = paste(Compartment, OTU_ID),
         Rank = rank(PercIncMSE)) 

rf.ranks %>% 
  ggplot(aes(reorder(cmpOTU, Rank), PercIncMSE, fill = PhyClass2)) +
  geom_bar(stat = "identity") +
  xlab("OTU") +
  scale_y_log10() +
  coord_flip() +
  scale_fill_manual(values = phy.pal) +
  facet_wrap(~Compartment, scales = "free") +
  theme_light() +
  theme(text = element_text(size = 15),
        axis.text.y = element_blank())

otu.rf <- train.set %>% 
  unnest(imp.tax) %>% 
  mutate(cmpOTU = paste(Compartment, OTU_ID),
         Rank = rank(PercIncMSE))

rs.id <- filter(otu.rf, Compartment == "Rhizosphere")$OTU_ID
es.id <- filter(otu.rf, Compartment == "Endosphere")$OTU_ID

# otu2 <- readRDS("../Data/otu_pers.RDS")
# otu2 <- rel_ab(otu2)
# otu2 <- log_norm(otu2)  
otu.tidy <- tidy_otu(otu)

get_order <- function(df, var) {
  mtrx <- df %>% 
    mutate(Group = paste(Compartment, Treatment, Time, sep = ".")) %>% 
    select(Group, var, OTU_ID) %>% 
    spread(key = Group, value = var)
  mtrx <- as.data.frame(mtrx)
  rownames(mtrx) <- mtrx$OTU_ID
  mtrx <- mtrx[,-1]
  dist.tmp <- dist(as.matrix(mtrx))
  dist.clust <- hclust(dist.tmp, method = "average")
  ord.names <- dist.clust$labels[dist.clust$order]
  clust.ord <- data.frame(OTU_ID = ord.names, order = 1:length(ord.names))
  df <- inner_join(df, clust.ord, by = "OTU_ID")
  df$order
}

get_cluster <- function(df, var, nclust) {
  mtrx <- df %>% 
    mutate(Group = paste(Compartment, Treatment, Time, sep = ".")) %>% 
    select(Group, var, OTU_ID) %>% 
    spread(key = Group, value = var)
  mtrx <- as.data.frame(mtrx)
  rownames(mtrx) <- mtrx$OTU_ID
  mtrx <- mtrx[,-1]
  dist.tmp <- dist(as.matrix(mtrx))
  dist.clust <- hclust(dist.tmp, method = "average")
  ord.names <- dist.clust$labels[dist.clust$order]
  clust.ord <- data.frame(OTU_ID = ord.names, order = 1:length(ord.names))
  clust.cut <- cutree(dist.clust[c(1,2,4)], k = nclust)
  clust.ord$Cluster <- as.factor(clust.cut[clust.ord$OTU_ID])
  df <- inner_join(df, clust.ord, by = "OTU_ID")
  df$Cluster
}
es.master.tidy <- otu.tidy %>% 
  inner_join(select(map, SampleID, Time, Compartment, Treatment), by = "SampleID") %>% 
  filter(Compartment == "ES") %>% 
  group_by(Compartment, Treatment, OTU_ID) %>% 
  mutate(zscore = (Count - mean(Count))/sd(Count)) %>% 
  group_by(Compartment, Treatment, Time, OTU_ID) %>% 
  summarise(MeanZS = mean(zscore)) %>% 
  ungroup()
`summarise()` regrouping output by 'Compartment', 'Treatment', 'Time' (override with `.groups` argument)
es.order <- es.master.tidy %>% 
  filter(OTU_ID %in% es.id) %>% 
  filter(Treatment == "WC") %>% 
  mutate(ZS_order = get_order(., "MeanZS"),
         ZS_cluster = get_cluster(., "MeanZS", 3)) %>% 
  mutate(Trend = case_when(
    ZS_cluster == 1 ~ "Complex",
    ZS_cluster == 2 ~ "Early Colonizer",
    ZS_cluster == 3 ~ "Late Colonizer"
  ))
Note: Using an external vector in selections is ambiguous.
ℹ Use `all_of(var)` instead of `var` to silence this message.
ℹ See <https://tidyselect.r-lib.org/reference/faq-external-vector.html>.
This message is displayed once per session.
es.plot.df <- es.master.tidy %>% 
  inner_join(select(es.order, OTU_ID, ZS_order, ZS_cluster, Trend), by = "OTU_ID") %>% 
  mutate(Treatment = fct_relevel(Treatment, "WC")) %>% 
  inner_join(tax, by = "OTU_ID") %>% 
  mutate(EndTreatment = case_when(
           Treatment == "WC" ~ 4.5,
           Treatment == "DS10" ~ 5.5,
           Treatment == "DS20" ~ 6.5,
           Treatment == "DS30" ~ 7.5,
         ))
rs.master.tidy <- otu.tidy %>% 
  inner_join(select(map, SampleID, Time, Compartment, Treatment), by = "SampleID") %>% 
  filter(Compartment == "RS") %>% 
  group_by(Compartment, Treatment, OTU_ID) %>% 
  mutate(zscore = (Count - mean(Count))/sd(Count)) %>% 
  group_by(Compartment, Treatment, Time, OTU_ID) %>% 
  summarise(MeanZS = mean(zscore)) %>% 
  ungroup()
`summarise()` regrouping output by 'Compartment', 'Treatment', 'Time' (override with `.groups` argument)
rs.order <- rs.master.tidy %>% 
  filter(OTU_ID %in% rs.id) %>% 
  filter(Treatment == "WC") %>% 
  mutate(ZS_order = get_order(., "MeanZS"),
        ZS_cluster = get_cluster(., "MeanZS", 3)) %>% 
  mutate(Trend = case_when(
    ZS_cluster == 3 ~ "Complex",
    ZS_cluster == 1 ~ "Early Colonizer",
    ZS_cluster == 2 ~ "Late Colonizer"
  ))

rs.plot.df <- rs.master.tidy %>% 
  inner_join(select(rs.order, OTU_ID, ZS_order, ZS_cluster, Trend), by = "OTU_ID") %>% 
  mutate(Treatment = fct_relevel(Treatment, "WC")) %>% 
  inner_join(tax, by = "OTU_ID") %>% 
  mutate(EndTreatment = case_when(
           Treatment == "WC" ~ 4.5,
           Treatment == "DS10" ~ 5.5,
           Treatment == "DS20" ~ 6.5,
           Treatment == "DS30" ~ 7.5,
         ))
all.plot.df <- rbind(rs.plot.df, es.plot.df) %>% 
  mutate(OTU_ID = paste(Compartment, OTU_ID))

all.ord <- rbind(group_by(rs.order, Compartment, OTU_ID, ZS_cluster, Trend) %>% count(),
                 group_by(es.order, Compartment, OTU_ID, ZS_cluster, Trend) %>% count())

all.ord$Trend <- as.factor(all.ord$Trend)

trend.p <- all.plot.df %>% 
  mutate(Treatment = fct_recode(Treatment, WC = "WC", DS1 = "D1", DS2 = "D2", DS3 = "D3")) %>% 
  mutate(Compartment = fct_relevel(Compartment, "RS")) %>% 
  mutate(Compartment = fct_recode(Compartment, Rhizosphere = "RS", Endosphere = "ES")) %>% 
  mutate(Trend = fct_relevel(Trend, "Complex", after = Inf)) %>% 
  ggplot(aes(Time * 10, reorder(OTU_ID, ZS_order), fill = MeanZS)) +
  geom_tile() +
  geom_point(aes(x = -1, color = Trend)) +
  geom_vline(data = trt.lines, aes(xintercept = (IniTreatment * 10)), linetype = 3) +
  geom_vline(data = trt.lines, aes(xintercept = (EndTreatment* 10)), linetype = 3) +
  scale_fill_gradientn(colors = RColorBrewer::brewer.pal(9, "Greys"),
                       name = "Relative Abundance\n(z-score)") +
  scale_color_manual(values = c("dodgerblue", "gold", "grey50")) +
  ylab("Age Discriminant OTU") +
  xlab("Plant Age (days)") +
  theme_classic() +
  theme(axis.text.y = element_blank(),
        panel.grid = element_blank(),
        text = element_text(size = 15),
        legend.position = "bottom",
        axis.line.y = element_blank(),
        axis.ticks.y = element_blank()) +
  facet_grid(Compartment ~ Treatment, scales = "free", space = "free") +
  guides(color = guide_legend(title.position = "top", title.hjust = 0.5))

trend.p

rf.otus <- rbind(rs.plot.df, es.plot.df) %>% 
  group_by(Compartment, OTU_ID, Trend, Kingdom, Phylum, Class, Order, Family, Genus, Species) %>% 
  count() %>% 
  select(-n) %>% 
  ungroup()

write.table(rf.otus, "../Tables/rf_otus.tsv", sep = "\t", quote = F, row.names = F)
overlap.plot <- all.plot.df  %>% 
  separate(OTU_ID, c("tmp", "OTU_ID")) %>% 
  select(OTU_ID, Compartment, Trend) %>% 
  mutate(RFTrend = Trend) %>% 
  select(-Trend) %>% 
  group_by(OTU_ID, Compartment, RFTrend) %>% 
  count() %>% 
  select(-n) %>% 
  left_join(all.clust.summary, by = c("Compartment", "OTU_ID")) %>% 
  ungroup() %>% 
  mutate(Compartment = fct_relevel(Compartment, "RS")) %>% 
  mutate(Compartment = fct_recode(Compartment, Rhizosphere = "RS", Endosphere = "ES")) %>% 
  mutate(Trend = ifelse(is.na(Trend), "None", Trend)) %>% 
  mutate(Trend = fct_recode(Trend, 
                            "Semi-Persistent\nEnrichment" = "Semi-Persistent Enrichment",
                            "Persistent\nDepletion" = "Persistent Depletion",
                            "Transient\nDepletion" = "Transient Depletion",
                            "Transient\nEnrichment" = "Transient Enrichment")) %>% 
  mutate(Trend = fct_relevel(Trend, "None", after = Inf)) %>% 
  mutate(RFTrend = fct_relevel(RFTrend, "Early Colonizer", after = Inf)) %>% 
  ggplot(aes(RFTrend, fill = Trend)) +
  geom_bar() +
  facet_grid(Compartment ~.) +
  ylab("nOTU") +
  xlab("") +
  scale_fill_manual(name = "Drought Module",
                    values = c(RColorBrewer::brewer.pal(4,"Dark2"), "gray69")) +
  guides(fill = guide_legend(ncol = 1)) +
  theme_classic() +
  theme(text = element_text(size = 15),
        legend.position = "right") +
  coord_flip()

overlap.plot

rf.tax <- all.plot.df %>% 
  mutate(Compartment = fct_recode(Compartment, Rhizosphere = "RS", Endosphere = "ES")) %>% 
  mutate(Compartment = fct_relevel(Compartment, "Rhizosphere")) %>% 
  mutate(Trend = fct_relevel(Trend, "Complex", after = Inf)) %>% 
  mutate(Trend = fct_recode(Trend, Early = "Early Colonizer", Late = "Late Colonizer")) %>% 
  group_by(Compartment, Trend, OTU_ID) %>% 
  count() %>% 
  separate(OTU_ID, c("tmp", "OTU_ID")) %>% 
  select(-n, -tmp) %>% 
  inner_join(tax, by = "OTU_ID") %>% 
  group_by(Compartment, Trend, PhyClass2, Phylum, Class, Order) %>% 
  count() %>%
  ggplot(aes(Trend, paste(Phylum, Class, Order, sep = " / "), fill = PhyClass2, label = n)) +
  geom_tile(color = "white", size = 1) +
  geom_text(color = "black", size = 3) +
  scale_fill_manual(name = "Taxon",
                    values = phy.pal,
                    limits = levels(tax$PhyClass2)) +
  ylab("Phylum / Class / Order") +
  facet_grid(.~Compartment) +
  theme_classic() +
  theme(text = element_text(size = 15),
        axis.text.x = element_text(angle = 45, hjust = 1),
        axis.text.y = element_text(size = 10),
        legend.position = "none",
        panel.grid.major = element_line(colour = "gray90"),
        axis.line.y = element_blank(),
        axis.ticks.y = element_blank())
  
rf.tax

supp.left <- plot_grid(cv.plot,
                       overlap.plot,
                       nrow = 1,
                       rel_widths = c(2,3),
                       labels = c("A", "B"),
                       label_size = 20)

supp.left


plot_grid(supp.left,
          trend.p,
          ncol = 1,
          rel_heights = c(1,3),
          labels = c(NA, "C"),
          label_size = 20)

Run random forest using the top OTUs as predictors

get_rf <- function(imp.predictors, response) {
  randomForest(imp.predictors, response, importance = F, keep.forest = T)
}

train.set <- train.set %>% 
  mutate(rf = map2(imp.otu, age, get_rf))


train.set %>% 
  filter(Compartment == "Rhizosphere") %>% 
  .$rf
[[1]]

Call:
 randomForest(x = imp.predictors, y = response, importance = F,      keep.forest = T) 
               Type of random forest: regression
                     Number of trees: 500
No. of variables tried at each split: 21

          Mean of squared residuals: 173.1405
                    % Var explained: 88.71
train.set %>% 
  filter(Compartment == "Endosphere") %>% 
  .$rf
[[1]]

Call:
 randomForest(x = imp.predictors, y = response, importance = F,      keep.forest = T) 
               Type of random forest: regression
                     Number of trees: 500
No. of variables tried at each split: 21

          Mean of squared residuals: 149.3839
                    % Var explained: 90.26

Use the whole dataset to get the predicted age of the plants

get_predict <- function(model, data) {
  predict(model, data)
} 

test.set <- whole.set %>% 
  inner_join(select(train.set, Compartment, rf), by = "Compartment") %>% 
  mutate(prediction = map2(rf, otu, get_predict)) 

rs.pred <- test.set %>% unnest(prediction, age) %>% filter(Treatment == "WC") %>% filter(Compartment == "Rhizosphere") %>% .$prediction
unnest() has a new interface. See ?unnest for details.
Try `df %>% unnest(c(prediction, age))`, with `mutate()` if needed
rs.age <- test.set %>% unnest(prediction, age) %>% filter(Treatment == "WC") %>% filter(Compartment == "Rhizosphere") %>% .$age
unnest() has a new interface. See ?unnest for details.
Try `df %>% unnest(c(prediction, age))`, with `mutate()` if needed
cor(rs.pred, rs.age)
[1] 0.9638256
es.pred <- test.set %>% unnest(prediction, age) %>% filter(Treatment == "WC") %>% filter(Compartment == "Endosphere") %>% .$prediction
unnest() has a new interface. See ?unnest for details.
Try `df %>% unnest(c(prediction, age))`, with `mutate()` if needed
es.age <- test.set %>% unnest(prediction, age) %>% filter(Treatment == "WC") %>% filter(Compartment == "Endosphere") %>% .$age
unnest() has a new interface. See ?unnest for details.
Try `df %>% unnest(c(prediction, age))`, with `mutate()` if needed
cor(es.pred, es.age)
[1] 0.9774801
test.set %>% 
  unnest(prediction, age) %>% 
  ggplot(aes(age, prediction)) +
  geom_point(aes(shape = Set, color = Treatment), alpha = 0.7) + 
  geom_abline(linetype = 2) + 
  geom_smooth(aes(color = Treatment), linetype = 2, stat = "smooth", se = F) +
  scale_color_manual(values = trt.pal) +
  facet_grid(Treatment + Set ~ Compartment) +
  theme_light() +
  theme(text = element_text(size = 20)) 
unnest() has a new interface. See ?unnest for details.
Try `df %>% unnest(c(prediction, age))`, with `mutate()` if needed

predictions <- test.set %>% 
  unnest(prediction, age) 
unnest() has a new interface. See ?unnest for details.
Try `df %>% unnest(c(prediction, age))`, with `mutate()` if needed
predictions.wc <- filter(predictions, Treatment == "WC" & Set == "Test") %>% select(-Treatment) %>% 
  ungroup()
Adding missing grouping variables: `Treatment`
predictions.wc.plotting <- rbind(select(predictions.wc, -Treatment) %>% mutate(Treatment = "DS1"),
                                 select(predictions.wc, -Treatment) %>% mutate(Treatment = "DS2"),
                                 select(predictions.wc, -Treatment) %>% mutate(Treatment = "DS3"))

predictions %>% 
  filter(Treatment != "WC") %>% 
  mutate(Treatment = str_replace(Treatment, "D", "DS")) %>% 
  mutate(Treatment = fct_relevel(Treatment, "WC")) %>% 
  ggplot(aes(age, prediction)) +
  geom_vline(data = trt.lines, aes(xintercept = IniTreatment2), linetype = 3) +
  geom_vline(data = trt.lines, aes(xintercept = EndTreatment2), linetype = 3) +
  geom_point(aes(color = Treatment), size = 2, shape = 1, alpha = 1) + 
  geom_smooth(data = predictions.wc.plotting, linetype = 2,  size = 1, stat = "smooth", se = F, color = trt.pal[1]) +
  xlab("Plant Age (days)") +
  ylab("Microbiome Age\n(days)") +
  scale_color_manual(values = trt.pal[-1]) +
  facet_grid(Compartment ~ Treatment) +
  theme_classic() +
  theme(text = element_text(size = 20),
        legend.position = "none") 
Unknown levels in `f`: WCUnknown levels in `f`: WCUnknown levels in `f`: WCUnknown levels in `f`: WCUnknown levels in `f`: WCUnknown levels in `f`: WC


loess.pred <- rbind(
  data.frame(loess = predict(loess(prediction~age,filter(predictions.wc, Compartment == "Rhizosphere")), unique(predictions.wc$age)),
             age = unique(predictions.wc$age),
             Compartment = "Rhizosphere"),
  data.frame(loess = predict(loess(prediction~age,filter(predictions.wc, Compartment == "Endosphere")), unique(predictions.wc$age)),
             age = unique(predictions.wc$age),
             Compartment = "Endosphere"))

predictions.wc %>% ggplot(aes(age, prediction, color = Compartment)) +
                      geom_smooth(linetype = 2,  size = 1, stat = "smooth", se = F) +
  geom_point(data = loess.pred, aes(age, loess))

NA
NA
library(broom)
library(contrast)
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio
time.age <- map %>% 
  group_by(Time, Age) %>% 
  count() %>% 
  select(-n) 

predictions <- inner_join(predictions, time.age, by = c("age" = "Age"))
predictions$TimeFctr <- as.factor(predictions$Time)

run_lm <- function(df) {
  lm(prediction ~ Treatment * TimeFctr, data = df)
}

run_anova <- function(fit) {
  anova(fit) %>% tidy()
}

get_contrasts <- function(fit) {
  contrasts <- vector(mode = "list")

  for(i in 1:13) {
    i <- as.character(i)
    print(i)
    contrasts[[paste("D1", i, sep = ".")]] <-  contrast(fit, list(Treatment = "WC", TimeFctr = i), list(Treatment = "D1", TimeFctr = i))[1:8] %>% as.data.frame()
    contrasts[[paste("D2", i, sep = ".")]] <-  contrast(fit, list(Treatment = "WC", TimeFctr = i), list(Treatment = "D2", TimeFctr = i))[1:8] %>% as.data.frame()
    contrasts[[paste("D3", i, sep = ".")]] <-  contrast(fit, list(Treatment = "WC", TimeFctr = i), list(Treatment = "D3", TimeFctr = i))[1:8] %>% as.data.frame()
  }
  
  contrasts <- plyr::ldply(contrasts, function(x) x)
  contrasts <- contrasts %>% 
    separate(.id, c("Treatment", "Time"))
}
predictions.nested <-predictions %>% 
  group_by(Compartment) %>% 
  nest() %>% 
  mutate(lm = map(data, run_lm),
         anova = map(lm, run_anova),
         contrasts = map(lm, get_contrasts))
[1] "1"
[1] "2"
[1] "3"
[1] "4"
[1] "5"
[1] "6"
[1] "7"
[1] "8"
[1] "9"
[1] "10"
[1] "11"
[1] "12"
[1] "13"
[1] "1"
[1] "2"
[1] "3"
[1] "4"
[1] "5"
[1] "6"
[1] "7"
[1] "8"
[1] "9"
[1] "10"
[1] "11"
[1] "12"
[1] "13"
rf.sig <- predictions.nested %>% 
  unnest(contrasts) %>% 
  group_by(Compartment, Treatment) %>% 
  mutate(p.adj = p.adjust(Pvalue, method = "fdr")) %>% 
  mutate(Time = as.integer(Time)) %>% 
  inner_join(time.age, by = "Time") 
a <- predictions %>% 
  filter(Treatment != "WC") %>% 
  mutate(Compartment = fct_relevel(Compartment, "RS", "ES")) %>% 
  mutate(Compartment = fct_recode(Compartment, Rhizosphere = "RS", Endosphere = "ES")) %>% 
  mutate(Treatment = str_replace(Treatment, "D", "DS")) %>% 
  ggplot(aes(age, prediction)) +
  geom_vline(data = trt.lines, aes(xintercept = IniTreatment2), linetype = 3) +
  geom_vline(data = trt.lines, aes(xintercept = EndTreatment2), linetype = 3) +
  geom_point(aes(color = Treatment), size = 2, shape = 1, alpha = 1) + 
  geom_smooth(data = predictions.wc.plotting, linetype = 2,  size = 1, stat = "smooth", se = F, color = trt.pal[1]) +
  xlab("Plant Age (days)") +
  ylab("Microbiome Age\n(days)") +
  scale_color_manual(values = trt.pal[-1]) +
  facet_grid(Compartment ~ Treatment) +
  theme_classic() +
  theme(text = element_text(size = 15),
        legend.position = "none") 
Unknown levels in `f`: RS, ESUnknown levels in `f`: RS, ESUnknown levels in `f`: RS, ESUnknown levels in `f`: RS, ESUnknown levels in `f`: RS, ESUnknown levels in `f`: RS, ESUnknown levels in `f`: RS, ESUnknown levels in `f`: RS, ESUnknown levels in `f`: RS, ESUnknown levels in `f`: RS, ESUnknown levels in `f`: RS, ESUnknown levels in `f`: RS, ES
a



b <- predictions %>%
  filter(Treatment != "WC") %>% 
  inner_join(loess.pred, by = c("Compartment", "age")) %>% 
  mutate(RelMat = (prediction - loess)) %>% 
  group_by(Compartment, Treatment, age) %>% 
  summarise(MeanMat = mean(RelMat),
            SDMat = sd(RelMat)) %>%
  mutate(SEMat = SDMat/2) %>% 
  inner_join(rf.sig, by = c("Compartment","Treatment", "age" = "Age")) %>% 
  ungroup() %>% 
  mutate(FDR = ifelse(p.adj < 0.05, "< 0.05", "≥ 0.05")) %>% 
  mutate(Compartment = fct_relevel(Compartment, "RS", "ES")) %>% 
  mutate(Compartment = fct_recode(Compartment, Rhizosphere = "RS", Endosphere = "ES")) %>% 
  mutate(Treatment = str_replace(Treatment, "D", "DS")) %>% 
  mutate(Treatment = fct_relevel(Treatment, "WC")) %>% 
  ggplot(aes(age, MeanMat, color = Treatment, shape = FDR)) +
  geom_point(size = 2) +
  geom_errorbar(aes(ymin = MeanMat - SEMat, ymax = MeanMat + SEMat)) +
  geom_vline(data = trt.lines, aes(xintercept = IniTreatment2), linetype = 3) +
  geom_vline(data = trt.lines, aes(xintercept = EndTreatment2), linetype = 3) +
  ylab("Relative Microbiome\nMaturity (days)") +
  xlab("Plant Age (days)") +
  facet_grid(Compartment ~ Treatment) +
  scale_color_manual(values = trt.pal[-1]) +
  scale_shape_manual(values = rev(ws.shp)) +
  theme_classic() +
  theme(text = element_text(size = 15),
        legend.position = "bottom") +
  guides(color = guide_legend(title.position = "top", title.hjust = 0.5),
         shape = guide_legend(title.position = "top", title.hjust = 0.5))
`summarise()` regrouping output by 'Compartment', 'Treatment' (override with `.groups` argument)
Unknown levels in `f`: RS, ESUnknown levels in `f`: RS, ESUnknown levels in `f`: WC
b

c <- otu.tidy %>% 
  mutate(Count = Count/100) %>% 
  inner_join(map, by = "SampleID") %>% 
  filter(Treatment2 != "WC_TRN") %>% 
  inner_join(all.ord, by = c("Compartment", "OTU_ID")) %>% 
  group_by(Compartment, Treatment, Age, Time, ZS_cluster, Trend,  SampleID) %>% 
  summarise(Total = sum(Count)) %>% 
  group_by(Compartment, Treatment, Age, Time, ZS_cluster, Trend) %>% 
  mutate(MeanAb = mean(Total)) %>% 
  ungroup() %>% 
  mutate(Trend = fct_relevel(Trend, "Complex", after = Inf)) %>% 
  mutate(Compartment = fct_relevel(Compartment, "RS", "ES")) %>% 
  mutate(Compartment = fct_recode(Compartment, Rhizosphere = "RS", Endosphere = "ES")) %>%
  mutate(Treatment = str_replace(Treatment, "D", "DS")) %>% 
  mutate(Treatment = fct_relevel(Treatment, "WC")) %>% 
  ggplot(aes(Age, Total, fill = Trend, color = Trend)) +
  geom_point(shape = 1, size = 2) +
  geom_smooth(se = F) +
  geom_vline(data = trt.lines, aes(xintercept = IniTreatment2), linetype = 3) +
  geom_vline(data = trt.lines, aes(xintercept = EndTreatment2), linetype = 3) +
  ylab("Relative Abundance") +
  xlab("Plant Age (days)") +
  scale_fill_manual(values = c("dodgerblue", "gold", "grey50")) +
  scale_color_manual(values = c("dodgerblue", "gold", "grey50")) +
  facet_grid(Treatment ~ Compartment) +
  theme_classic() +
  theme(text = element_text(size = 15),
        legend.position = "bottom") +
  guides(color = guide_legend(title.position = "top", title.hjust = 0.5),
         fill = guide_legend(title.position = "top", title.hjust = 0.5))
`summarise()` regrouping output by 'Compartment', 'Treatment', 'Age', 'Time', 'ZS_cluster', 'Trend' (override with `.groups` argument)
c

left <- plot_grid(a,b + theme(legend.position = "none"), get_legend(b),
                  nrow = 3,
                  rel_heights = c(4,4,1),
                  labels = c("A", "B"),
                  label_size = 20)
conversion failure on '≥ 0.05' in 'mbcsToSbcs': dot substituted for <e2>conversion failure on '≥ 0.05' in 'mbcsToSbcs': dot substituted for <89>conversion failure on '≥ 0.05' in 'mbcsToSbcs': dot substituted for <a5>conversion failure on '≥ 0.05' in 'mbcsToSbcs': dot substituted for <e2>conversion failure on '≥ 0.05' in 'mbcsToSbcs': dot substituted for <89>conversion failure on '≥ 0.05' in 'mbcsToSbcs': dot substituted for <a5>conversion failure on '≥ 0.05' in 'mbcsToSbcs': dot substituted for <e2>conversion failure on '≥ 0.05' in 'mbcsToSbcs': dot substituted for <89>conversion failure on '≥ 0.05' in 'mbcsToSbcs': dot substituted for <a5>conversion failure on '≥ 0.05' in 'mbcsToSbcs': dot substituted for <e2>conversion failure on '≥ 0.05' in 'mbcsToSbcs': dot substituted for <89>conversion failure on '≥ 0.05' in 'mbcsToSbcs': dot substituted for <a5>conversion failure on '≥ 0.05' in 'mbcsToSbcs': dot substituted for <e2>conversion failure on '≥ 0.05' in 'mbcsToSbcs': dot substituted for <89>conversion failure on '≥ 0.05' in 'mbcsToSbcs': dot substituted for <a5>conversion failure on '≥ 0.05' in 'mbcsToSbcs': dot substituted for <e2>conversion failure on '≥ 0.05' in 'mbcsToSbcs': dot substituted for <89>conversion failure on '≥ 0.05' in 'mbcsToSbcs': dot substituted for <a5>conversion failure on '≥ 0.05' in 'mbcsToSbcs': dot substituted for <e2>conversion failure on '≥ 0.05' in 'mbcsToSbcs': dot substituted for <89>conversion failure on '≥ 0.05' in 'mbcsToSbcs': dot substituted for <a5>conversion failure on '≥ 0.05' in 'mbcsToSbcs': dot substituted for <e2>conversion failure on '≥ 0.05' in 'mbcsToSbcs': dot substituted for <89>conversion failure on '≥ 0.05' in 'mbcsToSbcs': dot substituted for <a5>conversion failure on '≥ 0.05' in 'mbcsToSbcs': dot substituted for <e2>conversion failure on '≥ 0.05' in 'mbcsToSbcs': dot substituted for <89>conversion failure on '≥ 0.05' in 'mbcsToSbcs': dot substituted for <a5>conversion failure on '≥ 0.05' in 'mbcsToSbcs': dot substituted for <e2>conversion failure on '≥ 0.05' in 'mbcsToSbcs': dot substituted for <89>conversion failure on '≥ 0.05' in 'mbcsToSbcs': dot substituted for <a5>`geom_smooth()` using method = 'loess' and formula 'y ~ x'
right <- plot_grid(c + theme(legend.position = "none"), get_legend(c),
                   nrow = 2,
                   rel_heights = c(8,1),
                   labels = "C",
                   label_size = 20)
`geom_smooth()` using method = 'loess' and formula 'y ~ x'
`geom_smooth()` using method = 'loess' and formula 'y ~ x'
##1011:849
plot_grid(left, right,
          rel_widths = c(3,2))

LS0tCnRpdGxlOiAiUmFuZG9tIEZvcmVzdCBUcmFpbmluZyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKTG9hZCBsaWJyYXJpZXMKYGBge3J9CnNvdXJjZSgiLi4vR2VuZXJhbC9wYXJhbWV0ZXJzLlIiKQpzb3VyY2UoIi4uL0dlbmVyYWwvcm1iX2Z1bmN0aW9ucy5SIikKbGlicmFyeShjb3dwbG90KQpsaWJyYXJ5KHJhbmRvbUZvcmVzdCkKbGlicmFyeSh0aWR5dmVyc2UpCmBgYAoKTG9hZCBkYXRhCmBgYHtyfQptYXAgPC0gcmVhZFJEUygiLi4vRGF0YS9kcm91Z2h0X21hcC5SRFMiKQp0YXggPC0gcmVhZFJEUygiLi4vRGF0YS90YXguUkRTIikKb3R1IDwtIHJlYWRSRFMoIi4uL0RhdGEvb3R1X3BlcnMuUkRTIikgCnRyYWluLnNldCA8LSByZWFkUkRTKCIuLi9EYXRhL3JmX3Jlc3VsdHMuUkRTIikgJT4lIAogIG11dGF0ZShDb21wYXJ0bWVudCA9IGZjdF9yZWNvZGUoQ29tcGFydG1lbnQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlJoaXpvc3BoZXJlIiA9ICJSUyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRW5kb3NwaGVyZSIgPSAiRVMiKSkgJT4lIAogIG11dGF0ZShDb21wYXJ0bWVudCA9IGZjdF9yZWxldmVsKENvbXBhcnRtZW50LCAiUmhpem9zcGhlcmUiKSkKICAKYWxsLmNsdXN0LnN1bW1hcnkgPC0gcmVhZFJEUygiLi4vRGF0YS9kcm91Z2h0X2NsdXN0ZXJzLlJEUyIpCmBgYAoKTG9hZCBkYXRhCmBgYHtyfQojIyBHZXQgbG9nLXRyYW5zZm9ybWVkIHJlbGF0aXZlIGFidW5kYW5jZXMgZm9yIHRoZSBPVFUgdGFibGUKb3R1IDwtIHJlbF9hYihvdHUpCm90dSA8LSBsb2dfbm9ybShvdHUpCgptYXAgPC0gbXV0YXRlKG1hcCwgU2V0ID0gaWZlbHNlKFRyZWF0bWVudDIgPT0gIldDX1RSTiIsICJUcmFpbiIsICJUZXN0IikpCgojIFRoZSBwcmVkaWN0b3JzIG5lZWQgdG8gYmUgZm9ybWF0dGVkIGFzIGEgZGF0YSBmcmFtZSBvciBtYXRyaXgKZ2V0X3ByZWRpY3RvcnMgPC0gZnVuY3Rpb24oeCl7CiAgc2VsZWN0KHgsIC0oU2FtcGxlSUQ6QWdlKSkKfSAKCiMgVGhlIHJlc3BvbnNlIHZhcmlhYmxlIG5lZWRzIHRvIGJlIGZvcm1hdHRlZCBhcyBhIHZlY3RvcgpnZXRfcmVzcG9uc2UgPC0gZnVuY3Rpb24oeCkgewogIHNlbGVjdCh4LCBBZ2UpICU+JSAKICAuJEFnZQp9ICAKCndob2xlLnNldCA8LSBhcy5kYXRhLmZyYW1lKHQob3R1KSkgJT4lICAKICBtdXRhdGUoU2FtcGxlSUQgPSByb3duYW1lcyguKSkgJT4lIAogIGlubmVyX2pvaW4oc2VsZWN0KG1hcCwgU2FtcGxlSUQsIENvbXBhcnRtZW50LCBTZXQsIFRyZWF0bWVudCwgQWdlKSwgYnkgPSAiU2FtcGxlSUQiKSAlPiUgCiAgZ3JvdXBfYnkoU2V0LCBDb21wYXJ0bWVudCwgVHJlYXRtZW50KSAlPiUgCiAgbmVzdCgpICU+JSAKICBtdXRhdGUob3R1ID0gbWFwKGRhdGEsIGdldF9wcmVkaWN0b3JzKSwKICAgICAgICAgYWdlID0gbWFwKGRhdGEsIGdldF9yZXNwb25zZSkpICU+JSAKICBtdXRhdGUoQ29tcGFydG1lbnQgPSBmY3RfcmVjb2RlKENvbXBhcnRtZW50LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJSaGl6b3NwaGVyZSIgPSAiUlMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkVuZG9zcGhlcmUiID0gIkVTIikpICU+JSAKICBtdXRhdGUoQ29tcGFydG1lbnQgPSBmY3RfcmVsZXZlbChDb21wYXJ0bWVudCwgIlJoaXpvc3BoZXJlIikpCmBgYAoKYGBge3J9CnRydC5saW5lcyA8LSBkYXRhLmZyYW1lKFRyZWF0bWVudCA9IGMoIkRTMSIsICJEUzIiLCAiRFMzIiksCiAgICAgICAgICAgICAgICAgICAgICAgIFRyZWF0bWVudDIgPSBjKCJEUzEiLCAiRFMyIiwgIkRTMyIpLAogICAgICAgICAgICAgICAgICAgICAgICBDb250cmFzdCA9IGMoIldDIHZzIERTMSIsICJXQyB2cyBEUzIiLCAiV0MgdnMgRFMzIiksCiAgICAgICAgICAgICAgICAgICAgICAgIEluaVRyZWF0bWVudCA9IGMoNC41LCA0LjUsIDQuNSksCiAgICAgICAgICAgICAgICAgICAgICAgIEVuZFRyZWF0bWVudCA9IGMoNS41LCA2LjUsIDcuNSksCiAgICAgICAgICAgICAgICAgICAgICAgIEluaVRyZWF0bWVudDIgPSBjKDQxLDQxLDQxKSwKICAgICAgICAgICAgICAgICAgICAgICAgRW5kVHJlYXRtZW50MiA9IGMoNTIsNjIsNzQpKQpgYGAKCmBgYHtyfQpjdi5wbG90IDwtIHRyYWluLnNldCAlPiUgCiAgdW5uZXN0KGN2LCBuT1RVKSAlPiUgCiAgbXV0YXRlKENvbXBhcnRtZW50ID0gZmN0X3JlbGV2ZWwoQ29tcGFydG1lbnQsICJSUyIpKSAlPiUgCiAgbXV0YXRlKENvbXBhcnRtZW50ID0gZmN0X3JlY29kZShDb21wYXJ0bWVudCwgUmhpem9zcGhlcmUgPSAiUlMiLCBFbmRvc3BoZXJlID0gIkVTIikpICU+JSAKICBnZ3Bsb3QoYWVzKG5PVFUsIEVycm9yLCBjb2xvciA9IENvbXBhcnRtZW50KSkgKwogIGdlb21fbGluZShzaXplID0xKSArCiAgZ2VvbV9wb2ludChhZXMoYWxwaGEgPSBtaW5FcnJvciksIHNpemUgPSAxMCkgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBuT1RVLCBhbHBoYSA9IG1pbkVycm9yKSwgc2l6ZSA9IDYsIGNvbG9yID0gImJsYWNrIikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSByZXYoY21wLnBhbCkpICsKICBzY2FsZV9hbHBoYV9tYW51YWwodmFsdWVzID0gYygwLDEpKSArCiAgZ3VpZGVzKGFscGhhID0gRikgKwogIHNjYWxlX3hfbG9nMTAoKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gYygwLjUsIDAuOCksCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSAKCmN2LnBsb3QKYGBgCgoKQ2hlY2sgdGF4b25vbXkgb2YgdGhlIE9UVXMKYGBge3J9CnJmLnJhbmtzIDwtIHRyYWluLnNldCAlPiUgCiAgdW5uZXN0KGltcC50YXgpICU+JSAKICBtdXRhdGUoY21wT1RVID0gcGFzdGUoQ29tcGFydG1lbnQsIE9UVV9JRCksCiAgICAgICAgIFJhbmsgPSByYW5rKFBlcmNJbmNNU0UpKSAKCnJmLnJhbmtzICU+JSAKICBnZ3Bsb3QoYWVzKHJlb3JkZXIoY21wT1RVLCBSYW5rKSwgUGVyY0luY01TRSwgZmlsbCA9IFBoeUNsYXNzMikpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKwogIHhsYWIoIk9UVSIpICsKICBzY2FsZV95X2xvZzEwKCkgKwogIGNvb3JkX2ZsaXAoKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGh5LnBhbCkgKwogIGZhY2V0X3dyYXAofkNvbXBhcnRtZW50LCBzY2FsZXMgPSAiZnJlZSIpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCkpCmBgYApgYGB7cn0Kb3R1LnJmIDwtIHRyYWluLnNldCAlPiUgCiAgdW5uZXN0KGltcC50YXgpICU+JSAKICBtdXRhdGUoY21wT1RVID0gcGFzdGUoQ29tcGFydG1lbnQsIE9UVV9JRCksCiAgICAgICAgIFJhbmsgPSByYW5rKFBlcmNJbmNNU0UpKQoKcnMuaWQgPC0gZmlsdGVyKG90dS5yZiwgQ29tcGFydG1lbnQgPT0gIlJoaXpvc3BoZXJlIikkT1RVX0lECmVzLmlkIDwtIGZpbHRlcihvdHUucmYsIENvbXBhcnRtZW50ID09ICJFbmRvc3BoZXJlIikkT1RVX0lECgojIG90dTIgPC0gcmVhZFJEUygiLi4vRGF0YS9vdHVfcGVycy5SRFMiKQojIG90dTIgPC0gcmVsX2FiKG90dTIpCiMgb3R1MiA8LSBsb2dfbm9ybShvdHUyKSAgCm90dS50aWR5IDwtIHRpZHlfb3R1KG90dSkKCmdldF9vcmRlciA8LSBmdW5jdGlvbihkZiwgdmFyKSB7CiAgbXRyeCA8LSBkZiAlPiUgCiAgICBtdXRhdGUoR3JvdXAgPSBwYXN0ZShDb21wYXJ0bWVudCwgVHJlYXRtZW50LCBUaW1lLCBzZXAgPSAiLiIpKSAlPiUgCiAgICBzZWxlY3QoR3JvdXAsIHZhciwgT1RVX0lEKSAlPiUgCiAgICBzcHJlYWQoa2V5ID0gR3JvdXAsIHZhbHVlID0gdmFyKQogIG10cnggPC0gYXMuZGF0YS5mcmFtZShtdHJ4KQogIHJvd25hbWVzKG10cngpIDwtIG10cngkT1RVX0lECiAgbXRyeCA8LSBtdHJ4WywtMV0KICBkaXN0LnRtcCA8LSBkaXN0KGFzLm1hdHJpeChtdHJ4KSkKICBkaXN0LmNsdXN0IDwtIGhjbHVzdChkaXN0LnRtcCwgbWV0aG9kID0gImF2ZXJhZ2UiKQogIG9yZC5uYW1lcyA8LSBkaXN0LmNsdXN0JGxhYmVsc1tkaXN0LmNsdXN0JG9yZGVyXQogIGNsdXN0Lm9yZCA8LSBkYXRhLmZyYW1lKE9UVV9JRCA9IG9yZC5uYW1lcywgb3JkZXIgPSAxOmxlbmd0aChvcmQubmFtZXMpKQogIGRmIDwtIGlubmVyX2pvaW4oZGYsIGNsdXN0Lm9yZCwgYnkgPSAiT1RVX0lEIikKICBkZiRvcmRlcgp9CgpnZXRfY2x1c3RlciA8LSBmdW5jdGlvbihkZiwgdmFyLCBuY2x1c3QpIHsKICBtdHJ4IDwtIGRmICU+JSAKICAgIG11dGF0ZShHcm91cCA9IHBhc3RlKENvbXBhcnRtZW50LCBUcmVhdG1lbnQsIFRpbWUsIHNlcCA9ICIuIikpICU+JSAKICAgIHNlbGVjdChHcm91cCwgdmFyLCBPVFVfSUQpICU+JSAKICAgIHNwcmVhZChrZXkgPSBHcm91cCwgdmFsdWUgPSB2YXIpCiAgbXRyeCA8LSBhcy5kYXRhLmZyYW1lKG10cngpCiAgcm93bmFtZXMobXRyeCkgPC0gbXRyeCRPVFVfSUQKICBtdHJ4IDwtIG10cnhbLC0xXQogIGRpc3QudG1wIDwtIGRpc3QoYXMubWF0cml4KG10cngpKQogIGRpc3QuY2x1c3QgPC0gaGNsdXN0KGRpc3QudG1wLCBtZXRob2QgPSAiYXZlcmFnZSIpCiAgb3JkLm5hbWVzIDwtIGRpc3QuY2x1c3QkbGFiZWxzW2Rpc3QuY2x1c3Qkb3JkZXJdCiAgY2x1c3Qub3JkIDwtIGRhdGEuZnJhbWUoT1RVX0lEID0gb3JkLm5hbWVzLCBvcmRlciA9IDE6bGVuZ3RoKG9yZC5uYW1lcykpCiAgY2x1c3QuY3V0IDwtIGN1dHJlZShkaXN0LmNsdXN0W2MoMSwyLDQpXSwgayA9IG5jbHVzdCkKICBjbHVzdC5vcmQkQ2x1c3RlciA8LSBhcy5mYWN0b3IoY2x1c3QuY3V0W2NsdXN0Lm9yZCRPVFVfSURdKQogIGRmIDwtIGlubmVyX2pvaW4oZGYsIGNsdXN0Lm9yZCwgYnkgPSAiT1RVX0lEIikKICBkZiRDbHVzdGVyCn0KYGBgCgpgYGB7cn0KZXMubWFzdGVyLnRpZHkgPC0gb3R1LnRpZHkgJT4lIAogIGlubmVyX2pvaW4oc2VsZWN0KG1hcCwgU2FtcGxlSUQsIFRpbWUsIENvbXBhcnRtZW50LCBUcmVhdG1lbnQpLCBieSA9ICJTYW1wbGVJRCIpICU+JSAKICBmaWx0ZXIoQ29tcGFydG1lbnQgPT0gIkVTIikgJT4lIAogIGdyb3VwX2J5KENvbXBhcnRtZW50LCBUcmVhdG1lbnQsIE9UVV9JRCkgJT4lIAogIG11dGF0ZSh6c2NvcmUgPSAoQ291bnQgLSBtZWFuKENvdW50KSkvc2QoQ291bnQpKSAlPiUgCiAgZ3JvdXBfYnkoQ29tcGFydG1lbnQsIFRyZWF0bWVudCwgVGltZSwgT1RVX0lEKSAlPiUgCiAgc3VtbWFyaXNlKE1lYW5aUyA9IG1lYW4oenNjb3JlKSkgJT4lIAogIHVuZ3JvdXAoKQoKZXMub3JkZXIgPC0gZXMubWFzdGVyLnRpZHkgJT4lIAogIGZpbHRlcihPVFVfSUQgJWluJSBlcy5pZCkgJT4lIAogIGZpbHRlcihUcmVhdG1lbnQgPT0gIldDIikgJT4lIAogIG11dGF0ZShaU19vcmRlciA9IGdldF9vcmRlciguLCAiTWVhblpTIiksCiAgICAgICAgIFpTX2NsdXN0ZXIgPSBnZXRfY2x1c3RlciguLCAiTWVhblpTIiwgMykpICU+JSAKICBtdXRhdGUoVHJlbmQgPSBjYXNlX3doZW4oCiAgICBaU19jbHVzdGVyID09IDEgfiAiQ29tcGxleCIsCiAgICBaU19jbHVzdGVyID09IDIgfiAiRWFybHkgQ29sb25pemVyIiwKICAgIFpTX2NsdXN0ZXIgPT0gMyB+ICJMYXRlIENvbG9uaXplciIKICApKQoKZXMucGxvdC5kZiA8LSBlcy5tYXN0ZXIudGlkeSAlPiUgCiAgaW5uZXJfam9pbihzZWxlY3QoZXMub3JkZXIsIE9UVV9JRCwgWlNfb3JkZXIsIFpTX2NsdXN0ZXIsIFRyZW5kKSwgYnkgPSAiT1RVX0lEIikgJT4lIAogIG11dGF0ZShUcmVhdG1lbnQgPSBmY3RfcmVsZXZlbChUcmVhdG1lbnQsICJXQyIpKSAlPiUgCiAgaW5uZXJfam9pbih0YXgsIGJ5ID0gIk9UVV9JRCIpICU+JSAKICBtdXRhdGUoRW5kVHJlYXRtZW50ID0gY2FzZV93aGVuKAogICAgICAgICAgIFRyZWF0bWVudCA9PSAiV0MiIH4gNC41LAogICAgICAgICAgIFRyZWF0bWVudCA9PSAiRFMxMCIgfiA1LjUsCiAgICAgICAgICAgVHJlYXRtZW50ID09ICJEUzIwIiB+IDYuNSwKICAgICAgICAgICBUcmVhdG1lbnQgPT0gIkRTMzAiIH4gNy41LAogICAgICAgICApKQpgYGAKCmBgYHtyfQpycy5tYXN0ZXIudGlkeSA8LSBvdHUudGlkeSAlPiUgCiAgaW5uZXJfam9pbihzZWxlY3QobWFwLCBTYW1wbGVJRCwgVGltZSwgQ29tcGFydG1lbnQsIFRyZWF0bWVudCksIGJ5ID0gIlNhbXBsZUlEIikgJT4lIAogIGZpbHRlcihDb21wYXJ0bWVudCA9PSAiUlMiKSAlPiUgCiAgZ3JvdXBfYnkoQ29tcGFydG1lbnQsIFRyZWF0bWVudCwgT1RVX0lEKSAlPiUgCiAgbXV0YXRlKHpzY29yZSA9IChDb3VudCAtIG1lYW4oQ291bnQpKS9zZChDb3VudCkpICU+JSAKICBncm91cF9ieShDb21wYXJ0bWVudCwgVHJlYXRtZW50LCBUaW1lLCBPVFVfSUQpICU+JSAKICBzdW1tYXJpc2UoTWVhblpTID0gbWVhbih6c2NvcmUpKSAlPiUgCiAgdW5ncm91cCgpCgpycy5vcmRlciA8LSBycy5tYXN0ZXIudGlkeSAlPiUgCiAgZmlsdGVyKE9UVV9JRCAlaW4lIHJzLmlkKSAlPiUgCiAgZmlsdGVyKFRyZWF0bWVudCA9PSAiV0MiKSAlPiUgCiAgbXV0YXRlKFpTX29yZGVyID0gZ2V0X29yZGVyKC4sICJNZWFuWlMiKSwKICAgICAgICBaU19jbHVzdGVyID0gZ2V0X2NsdXN0ZXIoLiwgIk1lYW5aUyIsIDMpKSAlPiUgCiAgbXV0YXRlKFRyZW5kID0gY2FzZV93aGVuKAogICAgWlNfY2x1c3RlciA9PSAzIH4gIkNvbXBsZXgiLAogICAgWlNfY2x1c3RlciA9PSAxIH4gIkVhcmx5IENvbG9uaXplciIsCiAgICBaU19jbHVzdGVyID09IDIgfiAiTGF0ZSBDb2xvbml6ZXIiCiAgKSkKCnJzLnBsb3QuZGYgPC0gcnMubWFzdGVyLnRpZHkgJT4lIAogIGlubmVyX2pvaW4oc2VsZWN0KHJzLm9yZGVyLCBPVFVfSUQsIFpTX29yZGVyLCBaU19jbHVzdGVyLCBUcmVuZCksIGJ5ID0gIk9UVV9JRCIpICU+JSAKICBtdXRhdGUoVHJlYXRtZW50ID0gZmN0X3JlbGV2ZWwoVHJlYXRtZW50LCAiV0MiKSkgJT4lIAogIGlubmVyX2pvaW4odGF4LCBieSA9ICJPVFVfSUQiKSAlPiUgCiAgbXV0YXRlKEVuZFRyZWF0bWVudCA9IGNhc2Vfd2hlbigKICAgICAgICAgICBUcmVhdG1lbnQgPT0gIldDIiB+IDQuNSwKICAgICAgICAgICBUcmVhdG1lbnQgPT0gIkRTMTAiIH4gNS41LAogICAgICAgICAgIFRyZWF0bWVudCA9PSAiRFMyMCIgfiA2LjUsCiAgICAgICAgICAgVHJlYXRtZW50ID09ICJEUzMwIiB+IDcuNSwKICAgICAgICAgKSkKYGBgCgpgYGB7cn0KYWxsLnBsb3QuZGYgPC0gcmJpbmQocnMucGxvdC5kZiwgZXMucGxvdC5kZikgJT4lIAogIG11dGF0ZShPVFVfSUQgPSBwYXN0ZShDb21wYXJ0bWVudCwgT1RVX0lEKSkKCmFsbC5vcmQgPC0gcmJpbmQoZ3JvdXBfYnkocnMub3JkZXIsIENvbXBhcnRtZW50LCBPVFVfSUQsIFpTX2NsdXN0ZXIsIFRyZW5kKSAlPiUgY291bnQoKSwKICAgICAgICAgICAgICAgICBncm91cF9ieShlcy5vcmRlciwgQ29tcGFydG1lbnQsIE9UVV9JRCwgWlNfY2x1c3RlciwgVHJlbmQpICU+JSBjb3VudCgpKQoKYWxsLm9yZCRUcmVuZCA8LSBhcy5mYWN0b3IoYWxsLm9yZCRUcmVuZCkKCnRyZW5kLnAgPC0gYWxsLnBsb3QuZGYgJT4lIAogIG11dGF0ZShUcmVhdG1lbnQgPSBmY3RfcmVjb2RlKFRyZWF0bWVudCwgV0MgPSAiV0MiLCBEUzEgPSAiRDEiLCBEUzIgPSAiRDIiLCBEUzMgPSAiRDMiKSkgJT4lIAogIG11dGF0ZShDb21wYXJ0bWVudCA9IGZjdF9yZWxldmVsKENvbXBhcnRtZW50LCAiUlMiKSkgJT4lIAogIG11dGF0ZShDb21wYXJ0bWVudCA9IGZjdF9yZWNvZGUoQ29tcGFydG1lbnQsIFJoaXpvc3BoZXJlID0gIlJTIiwgRW5kb3NwaGVyZSA9ICJFUyIpKSAlPiUgCiAgbXV0YXRlKFRyZW5kID0gZmN0X3JlbGV2ZWwoVHJlbmQsICJDb21wbGV4IiwgYWZ0ZXIgPSBJbmYpKSAlPiUgCiAgZ2dwbG90KGFlcyhUaW1lICogMTAsIHJlb3JkZXIoT1RVX0lELCBaU19vcmRlciksIGZpbGwgPSBNZWFuWlMpKSArCiAgZ2VvbV90aWxlKCkgKwogIGdlb21fcG9pbnQoYWVzKHggPSAtMSwgY29sb3IgPSBUcmVuZCkpICsKICBnZW9tX3ZsaW5lKGRhdGEgPSB0cnQubGluZXMsIGFlcyh4aW50ZXJjZXB0ID0gKEluaVRyZWF0bWVudCAqIDEwKSksIGxpbmV0eXBlID0gMykgKwogIGdlb21fdmxpbmUoZGF0YSA9IHRydC5saW5lcywgYWVzKHhpbnRlcmNlcHQgPSAoRW5kVHJlYXRtZW50KiAxMCkpLCBsaW5ldHlwZSA9IDMpICsKICBzY2FsZV9maWxsX2dyYWRpZW50bihjb2xvcnMgPSBSQ29sb3JCcmV3ZXI6OmJyZXdlci5wYWwoOSwgIkdyZXlzIiksCiAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJSZWxhdGl2ZSBBYnVuZGFuY2Vcbih6LXNjb3JlKSIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiZG9kZ2VyYmx1ZSIsICJnb2xkIiwgImdyZXk1MCIpKSArCiAgeWxhYigiQWdlIERpc2NyaW1pbmFudCBPVFUiKSArCiAgeGxhYigiUGxhbnQgQWdlIChkYXlzKSIpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgdGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgICAgIGF4aXMubGluZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIGZhY2V0X2dyaWQoQ29tcGFydG1lbnQgfiBUcmVhdG1lbnQsIHNjYWxlcyA9ICJmcmVlIiwgc3BhY2UgPSAiZnJlZSIpICsKICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQodGl0bGUucG9zaXRpb24gPSAidG9wIiwgdGl0bGUuaGp1c3QgPSAwLjUpKQoKdHJlbmQucApgYGAKCmBgYHtyfQpyZi5vdHVzIDwtIHJiaW5kKHJzLnBsb3QuZGYsIGVzLnBsb3QuZGYpICU+JSAKICBncm91cF9ieShDb21wYXJ0bWVudCwgT1RVX0lELCBUcmVuZCwgS2luZ2RvbSwgUGh5bHVtLCBDbGFzcywgT3JkZXIsIEZhbWlseSwgR2VudXMsIFNwZWNpZXMpICU+JSAKICBjb3VudCgpICU+JSAKICBzZWxlY3QoLW4pICU+JSAKICB1bmdyb3VwKCkKCndyaXRlLnRhYmxlKHJmLm90dXMsICIuLi9UYWJsZXMvcmZfb3R1cy50c3YiLCBzZXAgPSAiXHQiLCBxdW90ZSA9IEYsIHJvdy5uYW1lcyA9IEYpCmBgYAoKCmBgYHtyfQpvdmVybGFwLnBsb3QgPC0gYWxsLnBsb3QuZGYgICU+JSAKICBzZXBhcmF0ZShPVFVfSUQsIGMoInRtcCIsICJPVFVfSUQiKSkgJT4lIAogIHNlbGVjdChPVFVfSUQsIENvbXBhcnRtZW50LCBUcmVuZCkgJT4lIAogIG11dGF0ZShSRlRyZW5kID0gVHJlbmQpICU+JSAKICBzZWxlY3QoLVRyZW5kKSAlPiUgCiAgZ3JvdXBfYnkoT1RVX0lELCBDb21wYXJ0bWVudCwgUkZUcmVuZCkgJT4lIAogIGNvdW50KCkgJT4lIAogIHNlbGVjdCgtbikgJT4lIAogIGxlZnRfam9pbihhbGwuY2x1c3Quc3VtbWFyeSwgYnkgPSBjKCJDb21wYXJ0bWVudCIsICJPVFVfSUQiKSkgJT4lIAogIHVuZ3JvdXAoKSAlPiUgCiAgbXV0YXRlKENvbXBhcnRtZW50ID0gZmN0X3JlbGV2ZWwoQ29tcGFydG1lbnQsICJSUyIpKSAlPiUgCiAgbXV0YXRlKENvbXBhcnRtZW50ID0gZmN0X3JlY29kZShDb21wYXJ0bWVudCwgUmhpem9zcGhlcmUgPSAiUlMiLCBFbmRvc3BoZXJlID0gIkVTIikpICU+JSAKICBtdXRhdGUoVHJlbmQgPSBpZmVsc2UoaXMubmEoVHJlbmQpLCAiTm9uZSIsIFRyZW5kKSkgJT4lIAogIG11dGF0ZShUcmVuZCA9IGZjdF9yZWNvZGUoVHJlbmQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNlbWktUGVyc2lzdGVudFxuRW5yaWNobWVudCIgPSAiU2VtaS1QZXJzaXN0ZW50IEVucmljaG1lbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBlcnNpc3RlbnRcbkRlcGxldGlvbiIgPSAiUGVyc2lzdGVudCBEZXBsZXRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIlRyYW5zaWVudFxuRGVwbGV0aW9uIiA9ICJUcmFuc2llbnQgRGVwbGV0aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICJUcmFuc2llbnRcbkVucmljaG1lbnQiID0gIlRyYW5zaWVudCBFbnJpY2htZW50IikpICU+JSAKICBtdXRhdGUoVHJlbmQgPSBmY3RfcmVsZXZlbChUcmVuZCwgIk5vbmUiLCBhZnRlciA9IEluZikpICU+JSAKICBtdXRhdGUoUkZUcmVuZCA9IGZjdF9yZWxldmVsKFJGVHJlbmQsICJFYXJseSBDb2xvbml6ZXIiLCBhZnRlciA9IEluZikpICU+JSAKICBnZ3Bsb3QoYWVzKFJGVHJlbmQsIGZpbGwgPSBUcmVuZCkpICsKICBnZW9tX2JhcigpICsKICBmYWNldF9ncmlkKENvbXBhcnRtZW50IH4uKSArCiAgeWxhYigibk9UVSIpICsKICB4bGFiKCIiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZSA9ICJEcm91Z2h0IE1vZHVsZSIsCiAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gYyhSQ29sb3JCcmV3ZXI6OmJyZXdlci5wYWwoNCwiRGFyazIiKSwgImdyYXk2OSIpKSArCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQobmNvbCA9IDEpKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikgKwogIGNvb3JkX2ZsaXAoKQoKb3ZlcmxhcC5wbG90CmBgYAoKYGBge3J9CnJmLnRheCA8LSBhbGwucGxvdC5kZiAlPiUgCiAgbXV0YXRlKENvbXBhcnRtZW50ID0gZmN0X3JlY29kZShDb21wYXJ0bWVudCwgUmhpem9zcGhlcmUgPSAiUlMiLCBFbmRvc3BoZXJlID0gIkVTIikpICU+JSAKICBtdXRhdGUoQ29tcGFydG1lbnQgPSBmY3RfcmVsZXZlbChDb21wYXJ0bWVudCwgIlJoaXpvc3BoZXJlIikpICU+JSAKICBtdXRhdGUoVHJlbmQgPSBmY3RfcmVsZXZlbChUcmVuZCwgIkNvbXBsZXgiLCBhZnRlciA9IEluZikpICU+JSAKICBtdXRhdGUoVHJlbmQgPSBmY3RfcmVjb2RlKFRyZW5kLCBFYXJseSA9ICJFYXJseSBDb2xvbml6ZXIiLCBMYXRlID0gIkxhdGUgQ29sb25pemVyIikpICU+JSAKICBncm91cF9ieShDb21wYXJ0bWVudCwgVHJlbmQsIE9UVV9JRCkgJT4lIAogIGNvdW50KCkgJT4lIAogIHNlcGFyYXRlKE9UVV9JRCwgYygidG1wIiwgIk9UVV9JRCIpKSAlPiUgCiAgc2VsZWN0KC1uLCAtdG1wKSAlPiUgCiAgaW5uZXJfam9pbih0YXgsIGJ5ID0gIk9UVV9JRCIpICU+JSAKICBncm91cF9ieShDb21wYXJ0bWVudCwgVHJlbmQsIFBoeUNsYXNzMiwgUGh5bHVtLCBDbGFzcywgT3JkZXIpICU+JSAKICBjb3VudCgpICU+JQogIGdncGxvdChhZXMoVHJlbmQsIHBhc3RlKFBoeWx1bSwgQ2xhc3MsIE9yZGVyLCBzZXAgPSAiIC8gIiksIGZpbGwgPSBQaHlDbGFzczIsIGxhYmVsID0gbikpICsKICBnZW9tX3RpbGUoY29sb3IgPSAid2hpdGUiLCBzaXplID0gMSkgKwogIGdlb21fdGV4dChjb2xvciA9ICJibGFjayIsIHNpemUgPSAzKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZSA9ICJUYXhvbiIsCiAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gcGh5LnBhbCwKICAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBsZXZlbHModGF4JFBoeUNsYXNzMikpICsKICB5bGFiKCJQaHlsdW0gLyBDbGFzcyAvIE9yZGVyIikgKwogIGZhY2V0X2dyaWQoLn5Db21wYXJ0bWVudCkgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiZ3JheTkwIiksCiAgICAgICAgYXhpcy5saW5lLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpKQogIApyZi50YXgKYGBgCmBgYHtyfQpzdXBwLmxlZnQgPC0gcGxvdF9ncmlkKGN2LnBsb3QsCiAgICAgICAgICAgICAgICAgICAgICAgb3ZlcmxhcC5wbG90LAogICAgICAgICAgICAgICAgICAgICAgIG5yb3cgPSAxLAogICAgICAgICAgICAgICAgICAgICAgIHJlbF93aWR0aHMgPSBjKDIsMyksCiAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiQSIsICJCIiksCiAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxfc2l6ZSA9IDIwKQoKc3VwcC5sZWZ0CgpwbG90X2dyaWQoc3VwcC5sZWZ0LAogICAgICAgICAgdHJlbmQucCwKICAgICAgICAgIG5jb2wgPSAxLAogICAgICAgICAgcmVsX2hlaWdodHMgPSBjKDEsMyksCiAgICAgICAgICBsYWJlbHMgPSBjKE5BLCAiQyIpLAogICAgICAgICAgbGFiZWxfc2l6ZSA9IDIwKQpgYGAKCgpSdW4gcmFuZG9tIGZvcmVzdCB1c2luZyB0aGUgdG9wIE9UVXMgYXMgcHJlZGljdG9ycwpgYGB7cn0KZ2V0X3JmIDwtIGZ1bmN0aW9uKGltcC5wcmVkaWN0b3JzLCByZXNwb25zZSkgewogIHJhbmRvbUZvcmVzdChpbXAucHJlZGljdG9ycywgcmVzcG9uc2UsIGltcG9ydGFuY2UgPSBGLCBrZWVwLmZvcmVzdCA9IFQpCn0KCnRyYWluLnNldCA8LSB0cmFpbi5zZXQgJT4lIAogIG11dGF0ZShyZiA9IG1hcDIoaW1wLm90dSwgYWdlLCBnZXRfcmYpKQoKCnRyYWluLnNldCAlPiUgCiAgZmlsdGVyKENvbXBhcnRtZW50ID09ICJSaGl6b3NwaGVyZSIpICU+JSAKICAuJHJmCgp0cmFpbi5zZXQgJT4lIAogIGZpbHRlcihDb21wYXJ0bWVudCA9PSAiRW5kb3NwaGVyZSIpICU+JSAKICAuJHJmCmBgYAoKVXNlIHRoZSB3aG9sZSBkYXRhc2V0IHRvIGdldCB0aGUgcHJlZGljdGVkIGFnZSBvZiB0aGUgcGxhbnRzCmBgYHtyfQpnZXRfcHJlZGljdCA8LSBmdW5jdGlvbihtb2RlbCwgZGF0YSkgewogIHByZWRpY3QobW9kZWwsIGRhdGEpCn0gCgp0ZXN0LnNldCA8LSB3aG9sZS5zZXQgJT4lIAogIGlubmVyX2pvaW4oc2VsZWN0KHRyYWluLnNldCwgQ29tcGFydG1lbnQsIHJmKSwgYnkgPSAiQ29tcGFydG1lbnQiKSAlPiUgCiAgbXV0YXRlKHByZWRpY3Rpb24gPSBtYXAyKHJmLCBvdHUsIGdldF9wcmVkaWN0KSkgCgpycy5wcmVkIDwtIHRlc3Quc2V0ICU+JSB1bm5lc3QocHJlZGljdGlvbiwgYWdlKSAlPiUgZmlsdGVyKFRyZWF0bWVudCA9PSAiV0MiKSAlPiUgZmlsdGVyKENvbXBhcnRtZW50ID09ICJSaGl6b3NwaGVyZSIpICU+JSAuJHByZWRpY3Rpb24KcnMuYWdlIDwtIHRlc3Quc2V0ICU+JSB1bm5lc3QocHJlZGljdGlvbiwgYWdlKSAlPiUgZmlsdGVyKFRyZWF0bWVudCA9PSAiV0MiKSAlPiUgZmlsdGVyKENvbXBhcnRtZW50ID09ICJSaGl6b3NwaGVyZSIpICU+JSAuJGFnZQpjb3IocnMucHJlZCwgcnMuYWdlKQoKZXMucHJlZCA8LSB0ZXN0LnNldCAlPiUgdW5uZXN0KHByZWRpY3Rpb24sIGFnZSkgJT4lIGZpbHRlcihUcmVhdG1lbnQgPT0gIldDIikgJT4lIGZpbHRlcihDb21wYXJ0bWVudCA9PSAiRW5kb3NwaGVyZSIpICU+JSAuJHByZWRpY3Rpb24KZXMuYWdlIDwtIHRlc3Quc2V0ICU+JSB1bm5lc3QocHJlZGljdGlvbiwgYWdlKSAlPiUgZmlsdGVyKFRyZWF0bWVudCA9PSAiV0MiKSAlPiUgZmlsdGVyKENvbXBhcnRtZW50ID09ICJFbmRvc3BoZXJlIikgJT4lIC4kYWdlCmNvcihlcy5wcmVkLCBlcy5hZ2UpCgp0ZXN0LnNldCAlPiUgCiAgdW5uZXN0KHByZWRpY3Rpb24sIGFnZSkgJT4lIAogIGdncGxvdChhZXMoYWdlLCBwcmVkaWN0aW9uKSkgKwogIGdlb21fcG9pbnQoYWVzKHNoYXBlID0gU2V0LCBjb2xvciA9IFRyZWF0bWVudCksIGFscGhhID0gMC43KSArIAogIGdlb21fYWJsaW5lKGxpbmV0eXBlID0gMikgKyAKICBnZW9tX3Ntb290aChhZXMoY29sb3IgPSBUcmVhdG1lbnQpLCBsaW5ldHlwZSA9IDIsIHN0YXQgPSAic21vb3RoIiwgc2UgPSBGKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHRydC5wYWwpICsKICBmYWNldF9ncmlkKFRyZWF0bWVudCArIFNldCB+IENvbXBhcnRtZW50KSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMjApKSAKYGBgCgoKYGBge3J9CnByZWRpY3Rpb25zIDwtIHRlc3Quc2V0ICU+JSAKICB1bm5lc3QocHJlZGljdGlvbiwgYWdlKSAKCnByZWRpY3Rpb25zLndjIDwtIGZpbHRlcihwcmVkaWN0aW9ucywgVHJlYXRtZW50ID09ICJXQyIgJiBTZXQgPT0gIlRlc3QiKSAlPiUgc2VsZWN0KC1UcmVhdG1lbnQpICU+JSAKICB1bmdyb3VwKCkKCnByZWRpY3Rpb25zLndjLnBsb3R0aW5nIDwtIHJiaW5kKHNlbGVjdChwcmVkaWN0aW9ucy53YywgLVRyZWF0bWVudCkgJT4lIG11dGF0ZShUcmVhdG1lbnQgPSAiRFMxIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdChwcmVkaWN0aW9ucy53YywgLVRyZWF0bWVudCkgJT4lIG11dGF0ZShUcmVhdG1lbnQgPSAiRFMyIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdChwcmVkaWN0aW9ucy53YywgLVRyZWF0bWVudCkgJT4lIG11dGF0ZShUcmVhdG1lbnQgPSAiRFMzIikpCgpwcmVkaWN0aW9ucyAlPiUgCiAgZmlsdGVyKFRyZWF0bWVudCAhPSAiV0MiKSAlPiUgCiAgbXV0YXRlKFRyZWF0bWVudCA9IHN0cl9yZXBsYWNlKFRyZWF0bWVudCwgIkQiLCAiRFMiKSkgJT4lIAogIG11dGF0ZShUcmVhdG1lbnQgPSBmY3RfcmVsZXZlbChUcmVhdG1lbnQsICJXQyIpKSAlPiUgCiAgZ2dwbG90KGFlcyhhZ2UsIHByZWRpY3Rpb24pKSArCiAgZ2VvbV92bGluZShkYXRhID0gdHJ0LmxpbmVzLCBhZXMoeGludGVyY2VwdCA9IEluaVRyZWF0bWVudDIpLCBsaW5ldHlwZSA9IDMpICsKICBnZW9tX3ZsaW5lKGRhdGEgPSB0cnQubGluZXMsIGFlcyh4aW50ZXJjZXB0ID0gRW5kVHJlYXRtZW50MiksIGxpbmV0eXBlID0gMykgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gVHJlYXRtZW50KSwgc2l6ZSA9IDIsIHNoYXBlID0gMSwgYWxwaGEgPSAxKSArIAogIGdlb21fc21vb3RoKGRhdGEgPSBwcmVkaWN0aW9ucy53Yy5wbG90dGluZywgbGluZXR5cGUgPSAyLCAgc2l6ZSA9IDEsIHN0YXQgPSAic21vb3RoIiwgc2UgPSBGLCBjb2xvciA9IHRydC5wYWxbMV0pICsKICB4bGFiKCJQbGFudCBBZ2UgKGRheXMpIikgKwogIHlsYWIoIk1pY3JvYmlvbWUgQWdlXG4oZGF5cykiKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHRydC5wYWxbLTFdKSArCiAgZmFjZXRfZ3JpZChDb21wYXJ0bWVudCB+IFRyZWF0bWVudCkgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMjApLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgCmBgYAoKCmBgYHtyfQoKbG9lc3MucHJlZCA8LSByYmluZCgKICBkYXRhLmZyYW1lKGxvZXNzID0gcHJlZGljdChsb2VzcyhwcmVkaWN0aW9ufmFnZSxmaWx0ZXIocHJlZGljdGlvbnMud2MsIENvbXBhcnRtZW50ID09ICJSaGl6b3NwaGVyZSIpKSwgdW5pcXVlKHByZWRpY3Rpb25zLndjJGFnZSkpLAogICAgICAgICAgICAgYWdlID0gdW5pcXVlKHByZWRpY3Rpb25zLndjJGFnZSksCiAgICAgICAgICAgICBDb21wYXJ0bWVudCA9ICJSaGl6b3NwaGVyZSIpLAogIGRhdGEuZnJhbWUobG9lc3MgPSBwcmVkaWN0KGxvZXNzKHByZWRpY3Rpb25+YWdlLGZpbHRlcihwcmVkaWN0aW9ucy53YywgQ29tcGFydG1lbnQgPT0gIkVuZG9zcGhlcmUiKSksIHVuaXF1ZShwcmVkaWN0aW9ucy53YyRhZ2UpKSwKICAgICAgICAgICAgIGFnZSA9IHVuaXF1ZShwcmVkaWN0aW9ucy53YyRhZ2UpLAogICAgICAgICAgICAgQ29tcGFydG1lbnQgPSAiRW5kb3NwaGVyZSIpKQoKcHJlZGljdGlvbnMud2MgJT4lIGdncGxvdChhZXMoYWdlLCBwcmVkaWN0aW9uLCBjb2xvciA9IENvbXBhcnRtZW50KSkgKwogICAgICAgICAgICAgICAgICAgICAgZ2VvbV9zbW9vdGgobGluZXR5cGUgPSAyLCAgc2l6ZSA9IDEsIHN0YXQgPSAic21vb3RoIiwgc2UgPSBGKSArCiAgZ2VvbV9wb2ludChkYXRhID0gbG9lc3MucHJlZCwgYWVzKGFnZSwgbG9lc3MpKQoKCmBgYApgYGB7cn0KbGlicmFyeShicm9vbSkKbGlicmFyeShjb250cmFzdCkKCnRpbWUuYWdlIDwtIG1hcCAlPiUgCiAgZ3JvdXBfYnkoVGltZSwgQWdlKSAlPiUgCiAgY291bnQoKSAlPiUgCiAgc2VsZWN0KC1uKSAKCnByZWRpY3Rpb25zIDwtIGlubmVyX2pvaW4ocHJlZGljdGlvbnMsIHRpbWUuYWdlLCBieSA9IGMoImFnZSIgPSAiQWdlIikpCnByZWRpY3Rpb25zJFRpbWVGY3RyIDwtIGFzLmZhY3RvcihwcmVkaWN0aW9ucyRUaW1lKQoKcnVuX2xtIDwtIGZ1bmN0aW9uKGRmKSB7CiAgbG0ocHJlZGljdGlvbiB+IFRyZWF0bWVudCAqIFRpbWVGY3RyLCBkYXRhID0gZGYpCn0KCnJ1bl9hbm92YSA8LSBmdW5jdGlvbihmaXQpIHsKICBhbm92YShmaXQpICU+JSB0aWR5KCkKfQoKZ2V0X2NvbnRyYXN0cyA8LSBmdW5jdGlvbihmaXQpIHsKICBjb250cmFzdHMgPC0gdmVjdG9yKG1vZGUgPSAibGlzdCIpCgogIGZvcihpIGluIDE6MTMpIHsKICAgIGkgPC0gYXMuY2hhcmFjdGVyKGkpCiAgICBwcmludChpKQogICAgY29udHJhc3RzW1twYXN0ZSgiRDEiLCBpLCBzZXAgPSAiLiIpXV0gPC0gIGNvbnRyYXN0KGZpdCwgbGlzdChUcmVhdG1lbnQgPSAiV0MiLCBUaW1lRmN0ciA9IGkpLCBsaXN0KFRyZWF0bWVudCA9ICJEMSIsIFRpbWVGY3RyID0gaSkpWzE6OF0gJT4lIGFzLmRhdGEuZnJhbWUoKQogICAgY29udHJhc3RzW1twYXN0ZSgiRDIiLCBpLCBzZXAgPSAiLiIpXV0gPC0gIGNvbnRyYXN0KGZpdCwgbGlzdChUcmVhdG1lbnQgPSAiV0MiLCBUaW1lRmN0ciA9IGkpLCBsaXN0KFRyZWF0bWVudCA9ICJEMiIsIFRpbWVGY3RyID0gaSkpWzE6OF0gJT4lIGFzLmRhdGEuZnJhbWUoKQogICAgY29udHJhc3RzW1twYXN0ZSgiRDMiLCBpLCBzZXAgPSAiLiIpXV0gPC0gIGNvbnRyYXN0KGZpdCwgbGlzdChUcmVhdG1lbnQgPSAiV0MiLCBUaW1lRmN0ciA9IGkpLCBsaXN0KFRyZWF0bWVudCA9ICJEMyIsIFRpbWVGY3RyID0gaSkpWzE6OF0gJT4lIGFzLmRhdGEuZnJhbWUoKQogIH0KICAKICBjb250cmFzdHMgPC0gcGx5cjo6bGRwbHkoY29udHJhc3RzLCBmdW5jdGlvbih4KSB4KQogIGNvbnRyYXN0cyA8LSBjb250cmFzdHMgJT4lIAogICAgc2VwYXJhdGUoLmlkLCBjKCJUcmVhdG1lbnQiLCAiVGltZSIpKQp9CmBgYAoKYGBge3J9CnByZWRpY3Rpb25zLm5lc3RlZCA8LXByZWRpY3Rpb25zICU+JSAKICBncm91cF9ieShDb21wYXJ0bWVudCkgJT4lIAogIG5lc3QoKSAlPiUgCiAgbXV0YXRlKGxtID0gbWFwKGRhdGEsIHJ1bl9sbSksCiAgICAgICAgIGFub3ZhID0gbWFwKGxtLCBydW5fYW5vdmEpLAogICAgICAgICBjb250cmFzdHMgPSBtYXAobG0sIGdldF9jb250cmFzdHMpKQoKcmYuc2lnIDwtIHByZWRpY3Rpb25zLm5lc3RlZCAlPiUgCiAgdW5uZXN0KGNvbnRyYXN0cykgJT4lIAogIGdyb3VwX2J5KENvbXBhcnRtZW50LCBUcmVhdG1lbnQpICU+JSAKICBtdXRhdGUocC5hZGogPSBwLmFkanVzdChQdmFsdWUsIG1ldGhvZCA9ICJmZHIiKSkgJT4lIAogIG11dGF0ZShUaW1lID0gYXMuaW50ZWdlcihUaW1lKSkgJT4lIAogIGlubmVyX2pvaW4odGltZS5hZ2UsIGJ5ID0gIlRpbWUiKSAlPiUgCiAgdW5ncm91cCgpCgoKYGBgCgpgYGB7cn0KYSA8LSBwcmVkaWN0aW9ucyAlPiUgCiAgZmlsdGVyKFRyZWF0bWVudCAhPSAiV0MiKSAlPiUgCiAgbXV0YXRlKENvbXBhcnRtZW50ID0gZmN0X3JlbGV2ZWwoQ29tcGFydG1lbnQsICJSUyIsICJFUyIpKSAlPiUgCiAgbXV0YXRlKENvbXBhcnRtZW50ID0gZmN0X3JlY29kZShDb21wYXJ0bWVudCwgUmhpem9zcGhlcmUgPSAiUlMiLCBFbmRvc3BoZXJlID0gIkVTIikpICU+JSAKICBtdXRhdGUoVHJlYXRtZW50ID0gc3RyX3JlcGxhY2UoVHJlYXRtZW50LCAiRCIsICJEUyIpKSAlPiUgCiAgZ2dwbG90KGFlcyhhZ2UsIHByZWRpY3Rpb24pKSArCiAgZ2VvbV92bGluZShkYXRhID0gdHJ0LmxpbmVzLCBhZXMoeGludGVyY2VwdCA9IEluaVRyZWF0bWVudDIpLCBsaW5ldHlwZSA9IDMpICsKICBnZW9tX3ZsaW5lKGRhdGEgPSB0cnQubGluZXMsIGFlcyh4aW50ZXJjZXB0ID0gRW5kVHJlYXRtZW50MiksIGxpbmV0eXBlID0gMykgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gVHJlYXRtZW50KSwgc2l6ZSA9IDIsIHNoYXBlID0gMSwgYWxwaGEgPSAxKSArIAogIGdlb21fc21vb3RoKGRhdGEgPSBwcmVkaWN0aW9ucy53Yy5wbG90dGluZywgbGluZXR5cGUgPSAyLCAgc2l6ZSA9IDEsIHN0YXQgPSAic21vb3RoIiwgc2UgPSBGLCBjb2xvciA9IHRydC5wYWxbMV0pICsKICB4bGFiKCJQbGFudCBBZ2UgKGRheXMpIikgKwogIHlsYWIoIk1pY3JvYmlvbWUgQWdlXG4oZGF5cykiKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHRydC5wYWxbLTFdKSArCiAgZmFjZXRfZ3JpZChDb21wYXJ0bWVudCB+IFRyZWF0bWVudCkgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgCgphCmBgYAoKCmBgYHtyfQoKCmIgPC0gcHJlZGljdGlvbnMgJT4lCiAgZmlsdGVyKFRyZWF0bWVudCAhPSAiV0MiKSAlPiUgCiAgaW5uZXJfam9pbihsb2Vzcy5wcmVkLCBieSA9IGMoIkNvbXBhcnRtZW50IiwgImFnZSIpKSAlPiUgCiAgbXV0YXRlKFJlbE1hdCA9IChwcmVkaWN0aW9uIC0gbG9lc3MpKSAlPiUgCiAgZ3JvdXBfYnkoQ29tcGFydG1lbnQsIFRyZWF0bWVudCwgYWdlKSAlPiUgCiAgc3VtbWFyaXNlKE1lYW5NYXQgPSBtZWFuKFJlbE1hdCksCiAgICAgICAgICAgIFNETWF0ID0gc2QoUmVsTWF0KSkgJT4lCiAgbXV0YXRlKFNFTWF0ID0gU0RNYXQvMikgJT4lIAogIGlubmVyX2pvaW4ocmYuc2lnLCBieSA9IGMoIkNvbXBhcnRtZW50IiwiVHJlYXRtZW50IiwgImFnZSIgPSAiQWdlIikpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIG11dGF0ZShGRFIgPSBpZmVsc2UocC5hZGogPCAwLjA1LCAiPCAwLjA1IiwgIuKJpSAwLjA1IikpICU+JSAKICBtdXRhdGUoQ29tcGFydG1lbnQgPSBmY3RfcmVsZXZlbChDb21wYXJ0bWVudCwgIlJTIiwgIkVTIikpICU+JSAKICBtdXRhdGUoQ29tcGFydG1lbnQgPSBmY3RfcmVjb2RlKENvbXBhcnRtZW50LCBSaGl6b3NwaGVyZSA9ICJSUyIsIEVuZG9zcGhlcmUgPSAiRVMiKSkgJT4lIAogIG11dGF0ZShUcmVhdG1lbnQgPSBzdHJfcmVwbGFjZShUcmVhdG1lbnQsICJEIiwgIkRTIikpICU+JSAKICBtdXRhdGUoVHJlYXRtZW50ID0gZmN0X3JlbGV2ZWwoVHJlYXRtZW50LCAiV0MiKSkgJT4lIAogIGdncGxvdChhZXMoYWdlLCBNZWFuTWF0LCBjb2xvciA9IFRyZWF0bWVudCwgc2hhcGUgPSBGRFIpKSArCiAgZ2VvbV9wb2ludChzaXplID0gMikgKwogIGdlb21fZXJyb3JiYXIoYWVzKHltaW4gPSBNZWFuTWF0IC0gU0VNYXQsIHltYXggPSBNZWFuTWF0ICsgU0VNYXQpKSArCiAgZ2VvbV92bGluZShkYXRhID0gdHJ0LmxpbmVzLCBhZXMoeGludGVyY2VwdCA9IEluaVRyZWF0bWVudDIpLCBsaW5ldHlwZSA9IDMpICsKICBnZW9tX3ZsaW5lKGRhdGEgPSB0cnQubGluZXMsIGFlcyh4aW50ZXJjZXB0ID0gRW5kVHJlYXRtZW50MiksIGxpbmV0eXBlID0gMykgKwogIHlsYWIoIlJlbGF0aXZlIE1pY3JvYmlvbWVcbk1hdHVyaXR5IChkYXlzKSIpICsKICB4bGFiKCJQbGFudCBBZ2UgKGRheXMpIikgKwogIGZhY2V0X2dyaWQoQ29tcGFydG1lbnQgfiBUcmVhdG1lbnQpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gdHJ0LnBhbFstMV0pICsKICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzID0gcmV2KHdzLnNocCkpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKwogIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZCh0aXRsZS5wb3NpdGlvbiA9ICJ0b3AiLCB0aXRsZS5oanVzdCA9IDAuNSksCiAgICAgICAgIHNoYXBlID0gZ3VpZGVfbGVnZW5kKHRpdGxlLnBvc2l0aW9uID0gInRvcCIsIHRpdGxlLmhqdXN0ID0gMC41KSkKYgpgYGAKCmBgYHtyfQpjIDwtIG90dS50aWR5ICU+JSAKICBtdXRhdGUoQ291bnQgPSBDb3VudC8xMDApICU+JSAKICBpbm5lcl9qb2luKG1hcCwgYnkgPSAiU2FtcGxlSUQiKSAlPiUgCiAgZmlsdGVyKFRyZWF0bWVudDIgIT0gIldDX1RSTiIpICU+JSAKICBpbm5lcl9qb2luKGFsbC5vcmQsIGJ5ID0gYygiQ29tcGFydG1lbnQiLCAiT1RVX0lEIikpICU+JSAKICBncm91cF9ieShDb21wYXJ0bWVudCwgVHJlYXRtZW50LCBBZ2UsIFRpbWUsIFpTX2NsdXN0ZXIsIFRyZW5kLCAgU2FtcGxlSUQpICU+JSAKICBzdW1tYXJpc2UoVG90YWwgPSBzdW0oQ291bnQpKSAlPiUgCiAgZ3JvdXBfYnkoQ29tcGFydG1lbnQsIFRyZWF0bWVudCwgQWdlLCBUaW1lLCBaU19jbHVzdGVyLCBUcmVuZCkgJT4lIAogIG11dGF0ZShNZWFuQWIgPSBtZWFuKFRvdGFsKSkgJT4lIAogIHVuZ3JvdXAoKSAlPiUgCiAgbXV0YXRlKFRyZW5kID0gZmN0X3JlbGV2ZWwoVHJlbmQsICJDb21wbGV4IiwgYWZ0ZXIgPSBJbmYpKSAlPiUgCiAgbXV0YXRlKENvbXBhcnRtZW50ID0gZmN0X3JlbGV2ZWwoQ29tcGFydG1lbnQsICJSUyIsICJFUyIpKSAlPiUgCiAgbXV0YXRlKENvbXBhcnRtZW50ID0gZmN0X3JlY29kZShDb21wYXJ0bWVudCwgUmhpem9zcGhlcmUgPSAiUlMiLCBFbmRvc3BoZXJlID0gIkVTIikpICU+JQogIG11dGF0ZShUcmVhdG1lbnQgPSBzdHJfcmVwbGFjZShUcmVhdG1lbnQsICJEIiwgIkRTIikpICU+JSAKICBtdXRhdGUoVHJlYXRtZW50ID0gZmN0X3JlbGV2ZWwoVHJlYXRtZW50LCAiV0MiKSkgJT4lIAogIGdncGxvdChhZXMoQWdlLCBUb3RhbCwgZmlsbCA9IFRyZW5kLCBjb2xvciA9IFRyZW5kKSkgKwogIGdlb21fcG9pbnQoc2hhcGUgPSAxLCBzaXplID0gMikgKwogIGdlb21fc21vb3RoKHNlID0gRikgKwogIGdlb21fdmxpbmUoZGF0YSA9IHRydC5saW5lcywgYWVzKHhpbnRlcmNlcHQgPSBJbmlUcmVhdG1lbnQyKSwgbGluZXR5cGUgPSAzKSArCiAgZ2VvbV92bGluZShkYXRhID0gdHJ0LmxpbmVzLCBhZXMoeGludGVyY2VwdCA9IEVuZFRyZWF0bWVudDIpLCBsaW5ldHlwZSA9IDMpICsKICB5bGFiKCJSZWxhdGl2ZSBBYnVuZGFuY2UiKSArCiAgeGxhYigiUGxhbnQgQWdlIChkYXlzKSIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJkb2RnZXJibHVlIiwgImdvbGQiLCAiZ3JleTUwIikpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiZG9kZ2VyYmx1ZSIsICJnb2xkIiwgImdyZXk1MCIpKSArCiAgZmFjZXRfZ3JpZChUcmVhdG1lbnQgfiBDb21wYXJ0bWVudCkgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKHRpdGxlLnBvc2l0aW9uID0gInRvcCIsIHRpdGxlLmhqdXN0ID0gMC41KSwKICAgICAgICAgZmlsbCA9IGd1aWRlX2xlZ2VuZCh0aXRsZS5wb3NpdGlvbiA9ICJ0b3AiLCB0aXRsZS5oanVzdCA9IDAuNSkpCgpjCmBgYApgYGB7cn0KbGVmdCA8LSBwbG90X2dyaWQoYSxiICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSwgZ2V0X2xlZ2VuZChiKSwKICAgICAgICAgICAgICAgICAgbnJvdyA9IDMsCiAgICAgICAgICAgICAgICAgIHJlbF9oZWlnaHRzID0gYyg0LDQsMSksCiAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIkEiLCAiQiIpLAogICAgICAgICAgICAgICAgICBsYWJlbF9zaXplID0gMjApCgpyaWdodCA8LSBwbG90X2dyaWQoYyArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiksIGdldF9sZWdlbmQoYyksCiAgICAgICAgICAgICAgICAgICBucm93ID0gMiwKICAgICAgICAgICAgICAgICAgIHJlbF9oZWlnaHRzID0gYyg4LDEpLAogICAgICAgICAgICAgICAgICAgbGFiZWxzID0gIkMiLAogICAgICAgICAgICAgICAgICAgbGFiZWxfc2l6ZSA9IDIwKQoKIyMxMDExOjg0OQpwbG90X2dyaWQobGVmdCwgcmlnaHQsCiAgICAgICAgICByZWxfd2lkdGhzID0gYygzLDIpKQpgYGAKCg==